﻿using System;
using System.Collections;
using System.Collections.Generic;

namespace Simple3DRedactor.Domain
{
    [Serializable]
    public sealed class Scene : IList<SceneObject>
    {
        private readonly List<SceneObject> _objects = new List<SceneObject>();

        public event Action<Scene> Change;
        private void OnChange()
        {
            if (Change != null) Change(this);
        }

        public event Action<Scene, SceneObject> ChildChange;
        private void OnChildChange(SceneObject obj)
        {
            if (ChildChange != null) ChildChange(this, obj);
            OnChange();
        }

        public event Action<Scene, SceneObject> ChildAdd;
        private void OnChildAdd(SceneObject obj)
        {
            if (ChildAdd != null) ChildAdd(this, obj);
            OnChange();
        }

        public event Action<Scene, SceneObject> ChildRemove;
        private void OnChildRemove(SceneObject obj)
        {
            if (ChildRemove != null) ChildRemove(this, obj);
            OnChange();
        }

        public event Action<Scene> ChildsClear;
        private void OnChildsClear()
        {
            if (ChildsClear != null) ChildsClear(this);
            OnChange();
        }

        public delegate void ChildAssignAction(Scene scene, SceneObject oldObj, SceneObject newObj);
        public event ChildAssignAction ChildAssign;
        private void OnChildAssign(SceneObject oldObj, SceneObject newObj)
        {
            if (ChildAssign != null) ChildAssign(this, oldObj, newObj);
            OnChange();
        }

        public IEnumerator<SceneObject> GetEnumerator()
        {
            return _objects.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public void Add(SceneObject item)
        {
            if (item == null) throw new ArgumentNullException("item");

            _objects.Add(item);

            item.Change += OnChildChange;

            OnChildAdd(item);
        }

        public void Clear()
        {
            foreach (var sceneObject in _objects)
                sceneObject.Change -= OnChildChange;

            _objects.Clear();

            OnChildsClear();
        }

        public bool Contains(SceneObject item)
        {
            return _objects.Contains(item);
        }

        public void CopyTo(SceneObject[] array, int arrayIndex)
        {
            _objects.CopyTo(array, arrayIndex);
        }

        public bool Remove(SceneObject item)
        {
            var res = _objects.Remove(item);

            if (res)
            {
                item.Change -= OnChildChange;

                OnChildRemove(item);
            }

            return res;
        }

        public int Count
        {
            get { return _objects.Count; }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        public int IndexOf(SceneObject item)
        {
            return _objects.IndexOf(item);
        }

        public void Insert(int index, SceneObject item)
        {
            _objects.Insert(index, item);

            item.Change -= OnChildChange;

            OnChildAdd(item);
        }

        public void RemoveAt(int index)
        {
            var obj = _objects[index];

            obj.Change -= OnChildChange;

            _objects.RemoveAt(index);

            OnChildRemove(obj);
        }

        public SceneObject this[int index]
        {
            get { return _objects[index]; }
            set
            {
                if (_objects[index] != value)
                {
                    var oldObj = _objects[index];
                    var newObj = _objects[index] = value;

                    oldObj.Change -= OnChildChange;
                    newObj.Change += OnChildChange;

                    OnChildAssign(oldObj, newObj);
                }
            }
        }
    }
}
